﻿using Ionic.Zip;
using Pdf2epub.Epub;
using Pdf2epub.Interfaces;
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;

namespace Pdf2epub.BusinessLogic
{
    internal class EpubExporter
    {
        private const string ContenOpfFile = "OPF/content.opf";
        private readonly string m_HtmlFolder;
        private IProjectContext m_ProjectContext;

        public EpubExporter(IProjectContext projectContext, string htmlFolder)
        {
            m_ProjectContext = projectContext;
            m_HtmlFolder = htmlFolder;
        }

        public string CoverHref { get; set; }

        public void Export(string epubOutputPath)
        {
            m_ProjectContext.FeedbackService.SetBusyText(string.Format("Rendering ePub {0}", epubOutputPath));

            if (!Directory.Exists(m_HtmlFolder))
                return;

            if (File.Exists(epubOutputPath))
                File.Delete(epubOutputPath);

            Epub.Epub epub2 = new Epub.Epub(m_ProjectContext, m_HtmlFolder, TocOptions.ByFirstH1Header)
                                       {
                                           DirectorySearchOption = SearchOption.AllDirectories
                                       };

            m_ProjectContext.FeedbackService.SetDetailText("Preparing Metadata");

            epub2.Metadata.Date = DateTime.Now.ToString(CultureInfo.InvariantCulture);
            epub2.Metadata.Language = m_ProjectContext.ProjectConfiguration.Language;
            epub2.Metadata.Title = m_ProjectContext.ProjectConfiguration.Title;
            epub2.Metadata.Subject = m_ProjectContext.ProjectConfiguration.Title;
            epub2.Metadata.Creator = m_ProjectContext.ProjectConfiguration.Author;

            m_ProjectContext.FeedbackService.SetDetailText("Building file...");

            epub2.BuildToFile(epubOutputPath);

            return;

            //var epub = new Document();
            //epub.AddAuthor(Controler.ProjectConfiguration.Author);
            //epub.AddTitle(Controler.ProjectConfiguration.Title);
            //epub.AddLanguage("en");

            //epub.AddFile(@"C:\Windows\Fonts\times.ttf", "fonts/times.ttf", "application/octet-stream");
            //epub.AddFile(@"C:\Windows\Fonts\times.ttf", "fonts/arial.ttf", "application/octet-stream");
            //epub.AddFile(@"C:\Windows\Fonts\times.ttf", "fonts/cour.ttf", "application/octet-stream");

            //string imageFolder = Path.Combine(m_HtmlFolder, Controler.HtmlImageSubfolder);
            //foreach (string imageFile in Directory.GetFiles(imageFolder))
            //{
            //    string filename = Path.GetFileName(imageFile);

            //    ProgressWindowHandler.SetDetailText(string.Format("Image file: {0}", filename));

            //    epub.AddImageFile(imageFile, filename);
            //}

            //if (!string.IsNullOrEmpty(CoverHref))
            //{
            //    CreateTitlePage();
            //    epub.AddXhtmlFile(string.Format("{0}/cover.html", m_HtmlFolder), "cover.html");
            //}

            //bool coverChapterSkipped = false;
            //int navCounter = 1;
            //foreach (string chapterFile in Directory.GetFiles(m_HtmlFolder, "cha*.html"))
            //{
            //    if (!coverChapterSkipped && Controler.ProjectConfiguration.FirstImageElementIsCoverImage && !string.IsNullOrEmpty(CoverHref))
            //    {
            //        coverChapterSkipped = true;
            //        continue;
            //    }

            //    string chapterFileName = Path.GetFileName(chapterFile);

            //    ProgressWindowHandler.SetDetailText(string.Format("Chapter file: {0}", chapterFileName));

            //    epub.AddXhtmlFile(chapterFile, chapterFileName);

            //    SubChapter mainChapter = FindChapters(chapterFile, 1, 1).FirstOrDefault();
            //    if (mainChapter != null)
            //    {
            //        NavPoint chapterTOCEntry = epub.AddNavPoint(mainChapter.Title, chapterFileName, navCounter++);
            //        foreach (SubChapter subChapter in FindChapters(chapterFile, 2, 3))
            //        {
            //            chapterTOCEntry.AddNavPoint(subChapter.Title,
            //                string.Format("{0}#{1}", chapterFileName, subChapter.Href),
            //                navCounter++);
            //        }
            //    }
            //}

            //ProgressWindowHandler.SetDetailText(string.Format("Saving file: {0}", Path.GetFileName(epubOutputPath)));
            //epub.Generate(epubOutputPath);

            //string outDir = Path.GetDirectoryName(epubOutputPath);

            //if (string.IsNullOrEmpty(CoverHref))
            //    return;

            //if (outDir != null)
            //{
            //    SetCoverImageInContentOpf(epubOutputPath, outDir);
            //}
        }

        private static bool ExtractContentOpf(string epubOutputPath, string contenOpfFile, string outDir)
        {
            bool contentOpfFound = false;
            using (ZipFile zip = ZipFile.Read(epubOutputPath))
            {
                foreach (ZipEntry e in zip)
                {
                    if (e.FileName == contenOpfFile)
                    {
                        e.Extract(outDir, ExtractExistingFileAction.OverwriteSilently);

                        contentOpfFound = true;
                        break;
                    }
                }
            }
            return contentOpfFound;
        }

        private static XmlNode FíndXmlNode(string nodeName, XmlNodeList childNodes)
        {
            return childNodes.Cast<XmlNode>().FirstOrDefault(node => node.Name.Equals(nodeName, StringComparison.InvariantCultureIgnoreCase));
        }

        private static bool IsCoverFound(XmlNode manifestNode)
        {
            bool coverFound = false;
            foreach (XmlNode itemNode in manifestNode.ChildNodes)
            {
                if (itemNode.Attributes == null)
                {
                    continue;
                }

                XmlAttribute xmlAttribute = itemNode.Attributes["id"];
                if (xmlAttribute.Value == "cover")
                {
                    coverFound = true;
                }
            }
            return coverFound;
        }

        private SubChapter CreateChapterFromHtmlHeader(XmlNode headerNode)
        {
            XmlElement anchorNode = FíndXmlNode("a", headerNode.ChildNodes) as XmlElement;
            XmlElement spanNode = FíndXmlNode("span", headerNode.ChildNodes) as XmlElement;

            if ((anchorNode != null) && (spanNode != null))
            {
                string anchorId = anchorNode.GetAttribute("id");
                string headerName = spanNode.InnerText.Trim();
                return new SubChapter(headerName, anchorId);
            }
            return null;
        }

        private void CreateTitlePage()
        {
            string coverImageFile = Path.Combine(m_ProjectContext.ProjectConfiguration.ImageOutputPath, CoverHref);
            if (!File.Exists(coverImageFile))
                return;

            XmlDocument xmlDoc = new XmlDocument();

            XmlElement htmlElement = xmlDoc.CreateElement("HTML");
            htmlElement.SetAttribute("xmlns", "http://www.w3.org/1999/xhtml");
            htmlElement.SetAttribute("xml:lang", "en");
            XmlNode htmlNode = xmlDoc.AppendChild(htmlElement);

            XmlElement headerElement = xmlDoc.CreateElement("HEAD");
            XmlNode headerNode = htmlNode.AppendChild(headerElement);

            XmlElement metaHttpElement = xmlDoc.CreateElement("META");
            metaHttpElement.SetAttribute("http-equiv", "Content-Type");
            metaHttpElement.SetAttribute("content", "text/html; charset=utf-8");
            headerNode.AppendChild(metaHttpElement);

            XmlElement metaCoverElement = xmlDoc.CreateElement("META");
            metaCoverElement.SetAttribute("name", "cover");
            headerNode.AppendChild(metaCoverElement);

            XmlElement metaTitleElement = xmlDoc.CreateElement("TITLE");
            metaTitleElement.InnerText = "Cover";
            headerNode.AppendChild(metaTitleElement);

            XmlElement metaStyleElement = xmlDoc.CreateElement("STYLE");
            metaStyleElement.SetAttribute("type", "text/css");

            XmlText styleText = xmlDoc.CreateTextNode("img { max-width: 100%; }");
            metaStyleElement.AppendChild(styleText);

            headerNode.AppendChild(metaStyleElement);

            XmlElement bodyElement = xmlDoc.CreateElement("BODY");
            XmlNode bodyNode = htmlNode.AppendChild(bodyElement);

            XmlElement divElement = xmlDoc.CreateElement("DIV");
            divElement.SetAttribute("id", "cover-image");
            XmlNode divNode = bodyNode.AppendChild(divElement);

            //XmlElement svgElement = xmlDoc.CreateElement("svg");
            //svgElement.SetAttribute("xmlns", "http://www.w3.org/2000/svg");
            //svgElement.SetAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
            //svgElement.SetAttribute("version", "1.1");
            //svgElement.SetAttribute("width", "100%");
            //svgElement.SetAttribute("height", "100%");
            //svgElement.SetAttribute("viewBox", string.Format("0 0 {0} {1}", imageWidth, imageHeight));
            //svgElement.SetAttribute("preserveAspectRatio", "none");
            //XmlNode svgNode = divNode.AppendChild(svgElement);

            XmlElement imageElement = xmlDoc.CreateElement("image");
            imageElement.SetAttribute("src", CoverHref);

            //svgNode.AppendChild(imageElement);
            divNode.AppendChild(imageElement);

            xmlDoc.Save(Path.Combine(m_HtmlFolder, "cover.html"));
        }

        private void InserCoverElement(XmlDocument opfXmlDoc, XmlNode manifestNode)
        {
            XmlElement coverElement = opfXmlDoc.CreateElement("item");
            coverElement.SetAttribute("id", "cover");
            coverElement.SetAttribute("media-type", "image/jpeg");
            coverElement.SetAttribute("href", CoverHref);
            manifestNode.AppendChild(coverElement);
        }
    }

    internal class SubChapter
    {
        public SubChapter(string title, string href)
        {
            Title = title;
            Href = href;
        }

        public string Href { get; private set; }

        public string Title { get; private set; }

        public override string ToString()
        {
            return string.Format("{0} (href:{1})", Title, Href);
        }
    }
}